/**
 * Copyright (C) 2013 Denatics Development
 *
 * Programmed by David Tiersch <dtiersch@denatics.com>
 *
 * This file is part of B0mberman.
 *
 * B0mberman is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * B0mberman is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with B0mberman. If not, see <http://www.gnu.org/licenses/>.
 */
package com.denatics.bomberman.control;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Observable;
import java.util.TreeMap;

import javax.swing.Timer;

import com.denatics.bomberman.Config;

/**
 * This Object listens to the Keyboard to notify on every change.
 *
 * Partially inspired by http://www.arco.in-berlin.de/keyevent.html
 *
 * @author David Tiersch <dtiersch@denatics.com>
 */
public final class KeyboardControl extends Observable implements KeyListener {
    /**
     * Delay of time to pass before a key is registered as pressed again.
     */
    private final int timerDelay;

    /**
     * Map which stores the times to the pressed keys.
     */
    private final TreeMap<Integer, TimedKey> map = new TreeMap<>();

    /**
     * Creates a new Keyboard Listener.
     */
    public KeyboardControl() {
        super();
        this.timerDelay = Config.KEYTIMERDELAY;
    }

    /*
     * (non-Javadoc)
     *
     * @see java.awt.event.KeyListener#keyTyped(java.awt.event.KeyEvent)
     */
    @Override
    public void keyTyped(final KeyEvent e) {
        // do nothing
    }

    /*
     * (non-Javadoc)
     *
     * @see java.awt.event.KeyListener#keyPressed(java.awt.event.KeyEvent)
     */
    @Override
    public void keyPressed(final KeyEvent e) {
        final int code = e.getKeyCode();
        final TimedKey tk = this.map.get(code);

        if (null == tk) {
            new TimedKey(code);

            this.setChanged();
            this.notifyObservers(new KeyAction(code, true));
        } else {
            tk.stopTimer();
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see java.awt.event.KeyListener#keyReleased(java.awt.event.KeyEvent)
     */
    @Override
    public void keyReleased(final KeyEvent e) {
        final TimedKey tk = this.map.get(e.getKeyCode());
        if (null != tk) {
            tk.restartTimer(e);
        }
    }

    /**
     * Class to store the keys and their time together.
     *
     * @author David Tiersch <dtiersch@denatics.com>
     */
    private final class TimedKey implements ActionListener {
        /**
         * Code of the key.
         */
        private final int keycode;

        /**
         * Timer to register differences in release an press.
         */
        private final Timer releaseTimer = new Timer(
                KeyboardControl.this.timerDelay, this);

        /**
         * Event of the Key released.
         */
        private KeyEvent releaseEvent;

        /**
         * Constructor to create the Class.
         *
         * @param code
         *            Keycode
         */
        private TimedKey(final int code) {
            this.keycode = code;
            KeyboardControl.this.map.put(code, this);
        }

        /**
         * Resets the Timer.
         *
         * @param e
         *            ReleaseEvent of the Key
         */
        public void restartTimer(final KeyEvent e) {
            this.releaseEvent = e;
            this.releaseTimer.restart();
        }

        /**
         * Resets the Storage.
         */
        public void stopTimer() {
            this.releaseTimer.stop();
            this.releaseEvent = null;
        }

        /*
         * (non-Javadoc)
         *
         * CHECKSTYLE IGNORE LineLength FOR 2 LINES
         *
         * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent
         * )
         */
        @Override
        public void actionPerformed(final ActionEvent e) {
            this.releaseTimer.stop();
            KeyboardControl.this.map.remove(this.keycode);
            KeyboardControl.this.setChanged();
            KeyboardControl.this.notifyObservers(new KeyAction(
                    this.releaseEvent.getKeyCode(), false));
            this.releaseEvent = null;
        }
    }
}
